import json
import ssl
import paho.mqtt.client as mqtt
from app import socketio
import os
from openai import OpenAI

# 存储最新的传感器数据
sensor_data = {
    "temperature": 25.5,
    "Humidity": 65.0,
    "WindSpeed": 2.3,
    "UV": 3.2,
    "AQI": 75,
    "LightLux": 1200,
    "soilHumidity": 45.5,
    "IrrigationPumpStatus": 0,
    "FanLevel": 0,
    "LEDLux": 0
}

# 存储控制设备的状态
device_status = {
    "IrrigationPumpStatus": 0,  # 水泵状态 0-关闭 1-开启
    "FanLevel": 2,              # 风扇级别 0-5
    "LEDLux": 3                # LED亮度 0-5
}

# 模拟数据生成函数
def generate_mock_data():
    import random
    import time
    import threading
    import math
    from datetime import datetime
    
    # 初始化时间相关变量
    start_time = time.time()
    
    def fluctuate_value(current_value, min_val, max_val, max_change, is_indoor=True, param_type=None):
        # 获取当前时间信息
        current_time = time.time()
        elapsed_hours = (current_time - start_time) / 3600  # 转换为小时
        current_datetime = datetime.now()
        hour_of_day = current_datetime.hour
        minute_of_hour = current_datetime.minute
        
        # 基础趋势值 - 根据参数类型和时间计算
        base_trend = 0
        
        # 室内环境波动更小
        if is_indoor:
            # 室内环境波动减小到原来的30%
            max_change = max_change * 0.3
        
        # 根据参数类型添加不同的时间趋势
        if param_type == 'temperature':
            # 温度在白天(8-18点)缓慢升高，晚上缓慢降低
            day_progress = 0
            if 8 <= hour_of_day < 18:
                # 白天时段内的进度(0-1)
                day_progress = (hour_of_day - 8 + minute_of_hour/60) / 10
                # 温度曲线：上午升高，下午略微下降
                base_trend = math.sin(day_progress * math.pi) * 1.5
            else:
                # 夜间时段
                if hour_of_day < 8:
                    night_progress = (hour_of_day + 24 - 18 + minute_of_hour/60) / 14
                else:  # hour_of_day >= 18
                    night_progress = (hour_of_day - 18 + minute_of_hour/60) / 14
                # 夜间温度缓慢下降后缓慢上升
                base_trend = -math.sin(night_progress * math.pi) * 1.0
        
        elif param_type == 'humidity':
            # 湿度与温度相反，温度高时湿度低
            if 8 <= hour_of_day < 18:
                day_progress = (hour_of_day - 8 + minute_of_hour/60) / 10
                # 湿度曲线：上午降低，下午略微升高
                base_trend = -math.sin(day_progress * math.pi) * 5
            else:
                if hour_of_day < 8:
                    night_progress = (hour_of_day + 24 - 18 + minute_of_hour/60) / 14
                else:  # hour_of_day >= 18
                    night_progress = (hour_of_day - 18 + minute_of_hour/60) / 14
                base_trend = math.sin(night_progress * math.pi) * 3
        
        elif param_type == 'light':
            # 光照强度随时间变化
            if 6 <= hour_of_day < 18:  # 白天
                day_progress = (hour_of_day - 6 + minute_of_hour/60) / 12
                # 光照曲线：早晨升高，中午最强，傍晚降低
                base_trend = math.sin(day_progress * math.pi) * 300
            else:  # 夜间
                base_trend = -200  # 夜间光照较低
        
        # 添加小幅随机波动
        random_fluctuation = random.normalvariate(0, max_change/3)
        
        # 计算新值 = 当前值 + 趋势变化 + 随机波动
        new_value = current_value + base_trend * 0.05 + random_fluctuation
        
        # 确保在范围内
        new_value = max(min_val, min(max_val, new_value))
        
        # 保留一位小数
        return round(new_value * 10) / 10
    
    def update_mock_data():
        global sensor_data, device_status
        
        while True:
            # 随机波动数据 - 室内环境参数变化更微小，并添加时间趋势
            # 室内温度范围更窄，波动更小，随时间变化
            sensor_data["temperature"] = fluctuate_value(sensor_data["temperature"], 22, 26, 0.2, True, 'temperature')
            # 室内湿度变化缓慢，与温度呈反相关
            sensor_data["Humidity"] = fluctuate_value(sensor_data["Humidity"], 45, 65, 1, True, 'humidity')
            # 室内风速很小，与温度相关
            sensor_data["WindSpeed"] = fluctuate_value(sensor_data["WindSpeed"], 0, 2, 0.1, True, 'wind')
            # 室内光照变化取决于窗户和灯光，随时间变化
            sensor_data["LightLux"] = fluctuate_value(sensor_data["LightLux"], 800, 1500, 50, True, 'light')
            # 盆栽土壤湿度变化缓慢，与浇水相关
            # 如果水泵开启，土壤湿度会缓慢上升
            if device_status["IrrigationPumpStatus"] == 1:
                # 水泵开启时，土壤湿度缓慢上升
                soil_change = 0.5  # 每次更新增加0.5%
                sensor_data["soilHumidity"] = min(55, sensor_data["soilHumidity"] + soil_change)
            else:
                # 水泵关闭时，土壤湿度缓慢下降
                sensor_data["soilHumidity"] = fluctuate_value(sensor_data["soilHumidity"], 35, 55, 0.5, True, 'soil')
                # 确保土壤湿度有缓慢下降趋势
                sensor_data["soilHumidity"] = max(35, sensor_data["soilHumidity"] - 0.1)
            
            # 室内UV很低，与光照相关
            sensor_data["UV"] = fluctuate_value(sensor_data["UV"], 0.5, 2, 0.1, True, 'uv')
            # 室内空气质量相对稳定，与风扇运行相关
            if device_status["FanLevel"] > 0:
                # 风扇开启时，空气质量缓慢改善
                aqi_change = device_status["FanLevel"] * 0.5  # 风扇档位越高，改善越快
                sensor_data["AQI"] = max(30, sensor_data["AQI"] - aqi_change)
            else:
                # 风扇关闭时，空气质量缓慢恶化
                sensor_data["AQI"] = round(fluctuate_value(sensor_data["AQI"], 30, 80, 2, True, 'aqi'))
                # 确保空气质量有缓慢上升趋势
                sensor_data["AQI"] = min(80, sensor_data["AQI"] + 0.2)
            
            # 发送更新的传感器数据到客户端
            socketio.emit('sensor_data_update', sensor_data)
            
            # 智能控制逻辑 - 调整为室内环境的控制逻辑
            if is_auto_control():
                # 土壤湿度低于38%时启动水泵
                if sensor_data["soilHumidity"] < 38:
                    device_status["IrrigationPumpStatus"] = 1
                else:
                    device_status["IrrigationPumpStatus"] = 0
                
                # 室内温度控制更精细
                # 22-23度：风扇0档
                # 23-24度：风扇1档
                # 24-25度：风扇2档
                # 25-26度：风扇3档
                # >26度：风扇4-5档
                temp = sensor_data["temperature"]
                if temp < 23:
                    device_status["FanLevel"] = 0
                elif temp < 24:
                    device_status["FanLevel"] = 1
                elif temp < 25:
                    device_status["FanLevel"] = 2
                elif temp < 26:
                    device_status["FanLevel"] = 3
                else:
                    device_status["FanLevel"] = min(int((temp - 26) * 2) + 4, 5)
                
                # 室内光照控制更精细
                # >1200：LED灯0档
                # 1000-1200：LED灯1档
                # 900-1000：LED灯2档
                # 800-900：LED灯3档
                # <800：LED灯4-5档
                light = sensor_data["LightLux"]
                if light > 1200:
                    device_status["LEDLux"] = 0
                elif light > 1000:
                    device_status["LEDLux"] = 1
                elif light > 900:
                    device_status["LEDLux"] = 2
                elif light > 800:
                    device_status["LEDLux"] = 3
                else:
                    device_status["LEDLux"] = min(int((800 - light) / 50) + 4, 5)
                
                # 发送更新的设备状态到客户端
                socketio.emit('device_status_update', device_status)
            
            # 每3秒更新一次
            time.sleep(3)
    
    # 启动模拟数据更新线程
    mock_thread = threading.Thread(target=update_mock_data, daemon=True)
    mock_thread.start()

# 判断是否为自动控制模式
def is_auto_control():
    # 这里可以根据实际需求设置自动控制的条件
    # 默认为自动控制模式
    return True

# 大模型对话历史
messages = [
    {'role': 'system', 'content': '你是一个智慧农业决策者，会给你提供当前室内温度(temperature)、湿度(Humidity)、风速(WindSpeed)、光照强度(LightLux)、土壤湿度(soilHumidity)、紫外线(UV)、空气质量(AQI)等数据，你要决策风扇转速(FanLevel， 取值范围0-5)、光照强度(LEDLux，取值范围0-5)、水泵开关(IrrigationPumpStatus，取值范围0-1)是否调整。'}, 
    {'role': 'system', 'content': '数据输出格式：{"FanLevel": 0, "LEDLux": 5, "IrrigationPumpStatus": 0}'}, 
    {'role': 'system', 'content': '如果输入的格式不对，或者提问无关的内容，你不能回复其他的内容，直接回复：{"FanLevel": 0, "LEDLux": 0, "IrrigationPumpStatus": 0}'}
]

# 阿里云提供的MQTT参数
params = {
    "clientId": "k01baZKR6Hv.Mqtt_Py|securemode=2,signmethod=hmacsha256,timestamp=1749290620231|",
    "username": "Mqtt_Py&k01baZKR6Hv",
    "mqttHostUrl": "iot-06z00ay6q2ji2ib.mqtt.iothub.aliyuncs.com",
    "passwd": "51b000af64e075170caa7b0f9f091a9269e634b5337bc6ddf584908531b901cc",
    "port": 1883
}

# 获取大模型响应
def get_response(messages):
    client = OpenAI(
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    )
    completion = client.chat.completions.create(
        model="deepseek-r1-0528",
        messages=messages
    )
    return completion

# MQTT客户端连接回调函数
def on_connect(client, userdata, flags, rc, properties=None):
    print(f"Connected with result code {rc}")
    # 订阅主题
    client.subscribe("/k01baZKR6Hv/Mqtt_Py/user/update")
    print("Subscribed to topic")

# MQTT客户端消息接收回调函数
def on_message(client, userdata, msg):
    global sensor_data, device_status
    try:
        payload = msg.payload.decode('utf-8')
        print(f"Received message: {payload}")
        
        json_data = json.loads(payload)
        if 'params' in json_data:
            params_data = json_data['params']
            
            # 更新传感器数据
            for key in params_data:
                if key in sensor_data:
                    sensor_data[key] = params_data[key]
            
            # 发送数据到Web客户端
            socketio.emit('sensor_data_update', sensor_data)
            
            # 调用大模型进行决策
            messages.append({'role': 'user', 'content': str(params_data)})
            assistant_output = get_response(messages).choices[0].message.content
            messages.append({'role': 'assistant', 'content': assistant_output})
            
            print(f'模型输出：{assistant_output}')
            
            # 解析模型输出并更新设备状态
            try:
                model_response = json.loads(assistant_output)
                for key in model_response:
                    if key in device_status:
                        device_status[key] = model_response[key]
                
                # 发送设备状态到Web客户端
                socketio.emit('device_status_update', device_status)
                
                # 发布设备控制命令
                publish_device_control(model_response)
            except json.JSONDecodeError:
                print("无法解析模型输出为JSON")
    except Exception as e:
        print(f"处理消息时出错: {str(e)}")

# 发布设备控制命令
def publish_device_control(control_data):
    try:
        json_data = json.dumps(control_data)
        result = client.publish("/k01baZKR6Hv/Mqtt_Py/user/get", json_data)
        if result.rc == 0:
            print(f"发布控制命令成功: {json_data}")
        else:
            print(f"发布控制命令失败: {result.rc}")
    except Exception as e:
        print(f"发布控制命令时出错: {str(e)}")

# 手动控制设备
def manual_control_device(control_data):
    global device_status
    try:
        # 更新设备状态
        for key, value in control_data.items():
            if key in device_status:
                device_status[key] = value
        
        # 发布设备控制命令
        publish_device_control(control_data)
        
        # 发送设备状态到Web客户端
        socketio.emit('device_status_update', device_status)
        
        return True, "控制命令已发送"
    except Exception as e:
        print(f"手动控制设备时出错: {str(e)}")
        return False, f"控制命令发送失败: {str(e)}"

# 创建MQTT客户端实例
client = mqtt.Client(client_id=params["clientId"], protocol=mqtt.MQTTv5)

# 设置连接回调函数
client.on_connect = on_connect
client.on_message = on_message

# 设置TLS连接参数
client.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_NONE, tls_version=ssl.PROTOCOL_TLS,
               ciphers=None)

# 设置用户名和密码
client.username_pw_set(params["username"], params["passwd"])

# 连接MQTT服务器
def connect_mqtt():
    try:
        client.connect(params["mqttHostUrl"], params["port"], 60)
        print("已连接到MQTT服务器")
        client.loop_start()
        return True
    except Exception as e:
        print(f"MQTT连接错误: {str(e)}")
        return False

# 断开与MQTT服务器的连接
def disconnect_mqtt():
    try:
        client.loop_stop()
        client.disconnect()
        print("已断开与MQTT服务器的连接")
        return True
    except Exception as e:
        print(f"MQTT断开连接错误: {str(e)}")
        return False